trello-mcp-server
by v4lheru
Verified
# Instructions for Creating a Trello MCP Server
This document provides comprehensive guidance for Language Models (LLMs) on how to create a Trello Model Context Protocol (MCP) server. These instructions emphasize modularity, organization, and maintainability as core requirements.
## Core Requirements
When creating a Trello MCP server, you **MUST** adhere to these fundamental principles:
1. **Modularity**: Break down functionality into logical, self-contained modules with clear responsibilities.
2. **Smaller Files**: Keep individual files focused and concise (ideally under 300 lines) to reduce cognitive load and make them easier to ingest.
3. **Clear Organization**: Follow a consistent directory structure that separates concerns.
4. **Type Safety**: Use TypeScript with proper type definitions throughout the codebase.
5. **Comprehensive Documentation**: Document all components, interfaces, and extension points.
## Project Structure
Organize your Trello MCP server using this structure:
```
trello-mcp-server/
├── src/
│ ├── services/ # Service classes for Trello API interactions
│ │ ├── base-service.ts # Abstract base service with common functionality
│ │ ├── board-service.ts # Board-related operations
│ │ ├── list-service.ts # List-related operations
│ │ ├── card-service.ts # Card-related operations
│ │ ├── member-service.ts # Member-related operations
│ │ ├── label-service.ts # Label-related operations
│ │ ├── checklist-service.ts # Checklist-related operations
│ │ ├── service-factory.ts # Factory for creating service instances
│ │ └── trello-service.ts # Main Trello service
│ ├── tools/ # MCP tool definitions and handlers
│ │ ├── board-tools.ts # Board tool definitions
│ │ ├── board-tool-handlers.ts # Board tool handlers
│ │ ├── list-tools.ts # List tool definitions
│ │ ├── list-tool-handlers.ts # List tool handlers
│ │ ├── card-tools.ts # Card tool definitions
│ │ ├── card-tool-handlers.ts # Card tool handlers
│ │ ├── member-tools.ts # Member tool definitions
│ │ ├── member-tool-handlers.ts # Member tool handlers
│ │ ├── label-tools.ts # Label tool definitions
│ │ ├── label-tool-handlers.ts # Label tool handlers
│ │ ├── checklist-tools.ts # Checklist tool definitions
│ │ ├── checklist-tool-handlers.ts # Checklist tool handlers
│ │ ├── trello-tools.ts # Combined tool definitions
│ │ └── trello-tool-handlers.ts # Combined tool handlers
│ ├── types/ # TypeScript type definitions
│ │ └── trello-types.ts # Trello-specific type definitions
│ ├── config.ts # Configuration management
│ └── index.ts # Main entry point
├── .env.example # Example environment variables
├── package.json # Project dependencies and scripts
├── tsconfig.json # TypeScript configuration
└── README.md # Project documentation
```
## Step-by-Step Implementation Guide
### 1. Set Up Project Structure
Begin by creating the directory structure and configuration files:
```bash
mkdir -p src/{services,tools,types}
touch src/config.ts src/index.ts
```
### 2. Define Configuration Management
Create a centralized configuration system in `config.ts` that:
- Loads environment variables
- Provides type-safe access to configuration
- Validates required settings
- Handles command-line arguments
Example:
```typescript
// src/config.ts
import dotenv from 'dotenv';
// Load environment variables
dotenv.config();
export interface Config {
trello: {
apiKey: string;
token: string;
apiUrl: string;
}
}
// Create and validate configuration
const config: Config = {
trello: {
apiKey: process.env.TRELLO_API_KEY || '',
token: process.env.TRELLO_TOKEN || '',
apiUrl: process.env.TRELLO_API_URL || 'https://api.trello.com/1'
}
};
// Validate required fields
const missingEnvVars = [];
if (!config.trello.apiKey) missingEnvVars.push('TRELLO_API_KEY');
if (!config.trello.token) missingEnvVars.push('TRELLO_TOKEN');
if (missingEnvVars.length > 0) {
throw new Error(`Missing required environment variables: ${missingEnvVars.join(', ')}`);
}
export default config;
```
### 3. Create Base Service Class
Implement a base service class that handles common functionality:
- HTTP requests with axios
- Error handling
- Rate limiting
- Logging
Example:
```typescript
// src/services/base-service.ts
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
export abstract class BaseService {
protected client: AxiosInstance;
protected apiKey: string;
protected token: string;
constructor(apiKey: string, token: string, baseURL: string) {
this.apiKey = apiKey;
this.token = token;
this.client = axios.create({
baseURL,
params: {
key: this.apiKey,
token: this.token
}
});
// Add interceptors for error handling, rate limiting, etc.
this.client.interceptors.response.use(
response => response,
error => {
// Handle rate limiting, authentication errors, etc.
console.error('API Error:', error.response?.data || error.message);
return Promise.reject(error);
}
);
}
// Common methods for API requests
protected async get<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.get<T>(path, config);
return response.data;
}
protected async post<T>(path: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.post<T>(path, data, config);
return response.data;
}
protected async put<T>(path: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.put<T>(path, data, config);
return response.data;
}
protected async delete<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.delete<T>(path, config);
return response.data;
}
}
```
### 4. Implement Trello-Specific Services
Create service classes that extend the base service and implement Trello-specific functionality:
```typescript
// src/services/board-service.ts
import { BaseService } from './base-service.js';
import { Board, BoardCreateParams, BoardUpdateParams } from '../types/trello-types.js';
export class BoardService extends BaseService {
async getBoards(filter?: string): Promise<Board[]> {
return this.get<Board[]>(`/members/me/boards${filter ? `?filter=${filter}` : ''}`);
}
async getBoard(boardId: string): Promise<Board> {
return this.get<Board>(`/boards/${boardId}`);
}
async createBoard(params: BoardCreateParams): Promise<Board> {
return this.post<Board>('/boards', params);
}
async updateBoard(boardId: string, params: BoardUpdateParams): Promise<Board> {
return this.put<Board>(`/boards/${boardId}`, params);
}
async deleteBoard(boardId: string): Promise<void> {
return this.delete<void>(`/boards/${boardId}`);
}
// Other board-related methods...
}
```
### 5. Define Type Definitions
Create clear type definitions for Trello objects:
```typescript
// src/types/trello-types.ts
export interface Board {
id: string;
name: string;
desc: string;
closed: boolean;
idOrganization: string;
url: string;
// Other properties...
}
export interface BoardCreateParams {
name: string;
desc?: string;
idOrganization?: string;
defaultLists?: boolean;
// Other properties...
}
export interface BoardUpdateParams {
name?: string;
desc?: string;
closed?: boolean;
// Other properties...
}
// Define other Trello types (List, Card, Member, etc.)
```
### 6. Define MCP Tools
Define the tools that your Trello MCP server will expose:
```typescript
// src/tools/board-tools.ts
export const boardTools = [
{
name: "get_boards",
description: "Retrieve a list of boards for the authenticated user. Use this tool to get an overview of available boards or to search for specific ones using filters.",
inputSchema: {
type: "object",
properties: {
filter: {
type: "string",
enum: ["all", "closed", "members", "open", "organization", "public", "starred", "unpinned"],
description: "Filter boards by status or membership"
},
fields: {
type: "array",
items: {
type: "string"
},
description: "Specific fields to include in the response (default: all fields)"
}
}
}
},
{
name: "get_board",
description: "Retrieve detailed information about a specific board by ID. Use this when you need comprehensive details about a particular board.",
inputSchema: {
type: "object",
properties: {
boardId: {
type: "string",
description: "ID of the board to retrieve"
},
fields: {
type: "array",
items: {
type: "string"
},
description: "Specific fields to include in the response (default: all fields)"
}
},
required: ["boardId"]
}
},
// Other board tools...
];
```
### 7. Implement Tool Handlers
Create handlers for each tool that process arguments and call service methods:
```typescript
// src/tools/board-tool-handlers.ts
import { BoardService } from '../services/board-service.js';
import { ServiceFactory } from '../services/service-factory.js';
export const boardToolHandlers = {
get_boards: async (args: any) => {
const boardService = ServiceFactory.getBoardService();
try {
const boards = await boardService.getBoards(args.filter);
return boards;
} catch (error) {
console.error('Error in get_boards:', error);
throw new Error(`Failed to get boards: ${error instanceof Error ? error.message : String(error)}`);
}
},
get_board: async (args: any) => {
const boardService = ServiceFactory.getBoardService();
try {
const board = await boardService.getBoard(args.boardId);
return board;
} catch (error) {
console.error('Error in get_board:', error);
throw new Error(`Failed to get board: ${error instanceof Error ? error.message : String(error)}`);
}
},
// Other board tool handlers...
};
```
### 8. Create Main Entry Point
Implement the main entry point that initializes services, registers tools, and starts the server:
```typescript
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ListResourceTemplatesRequestSchema,
ListPromptsRequestSchema,
McpError,
ErrorCode
} from "@modelcontextprotocol/sdk/types.js";
import config from "./config.js";
import { ServiceFactory } from "./services/service-factory.js";
import { trelloTools } from "./tools/trello-tools.js";
import { trelloToolHandlers } from "./tools/trello-tool-handlers.js";
// Redirect console output to stderr to avoid interfering with MCP protocol
const originalConsoleLog = console.log;
const originalConsoleError = console.error;
console.log = (...args) => {
process.stderr.write(`[INFO] ${args.join(' ')}\n`);
};
console.error = (...args) => {
process.stderr.write(`[ERROR] ${args.join(' ')}\n`);
};
async function main() {
try {
console.log("Initializing Trello MCP Server...");
// Initialize services
const serviceFactory = ServiceFactory.initialize(
config.trello.apiKey,
config.trello.token
);
// Create MCP server
const server = new Server(
{
name: "trello-mcp-server",
version: "0.1.0",
},
{
capabilities: {
tools: {},
resources: {},
prompts: {}
}
}
);
// Set up error handling
server.onerror = (error) => {
console.error("[MCP Server Error]", error);
};
// Handle process termination
process.on("SIGINT", async () => {
console.log("Shutting down server...");
await server.close();
process.exit(0);
});
// Register tool list handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: trelloTools
};
});
// Register resources list handler (empty implementation)
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: []
};
});
// Register resource templates list handler (empty implementation)
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
return {
resourceTemplates: []
};
});
// Register prompts list handler (empty implementation)
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: []
};
});
// Register tool call handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const toolName = request.params.name;
const handler = trelloToolHandlers[toolName as keyof typeof trelloToolHandlers];
if (!handler) {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${toolName}`
);
}
// Execute the tool handler with the provided arguments
const result = await handler(request.params.arguments);
// Return the result
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
} catch (error) {
console.error("Error handling tool call:", error);
// If it's already an MCP error, rethrow it
if (error instanceof McpError) {
throw error;
}
// Otherwise, wrap it in an MCP error
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}
],
isError: true
};
}
});
// Connect to transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.log("Trello MCP Server running on stdio");
} catch (error) {
console.error("Failed to start server:", error);
process.exit(1);
}
}
// Start the server
main().catch(console.error);
```
## Best Practices for Trello MCP Server Development
### Modularity Guidelines
1. **Single Responsibility Principle**: Each service class should handle one type of Trello entity (boards, lists, cards, etc.).
2. **Interface Segregation**: Define clear interfaces between components.
3. **Dependency Injection**: Use the ServiceFactory to create and manage service instances.
4. **Separation of Concerns**: Keep tool definitions separate from their implementations.
### File Size and Organization
1. **Keep Files Small**: Aim for files under 300 lines of code.
2. **Logical Grouping**: Group related functionality in the same directory.
3. **Consistent Naming**: Use a consistent naming convention for files and directories.
4. **Export Patterns**: Be explicit about what each module exports.
### Error Handling
1. **Comprehensive Error Handling**: Handle errors at all levels (service, tool, server).
2. **Informative Error Messages**: Provide clear error messages that help diagnose issues.
3. **Error Propagation**: Properly propagate errors up the call stack.
4. **Logging**: Log errors with appropriate context for debugging.
### Documentation
1. **JSDoc Comments**: Document all classes, methods, and interfaces.
2. **README**: Provide comprehensive documentation on how to use and extend the server.
3. **Code Examples**: Include examples of how to add new tools and services.
4. **Architecture Overview**: Document the overall architecture and design decisions.
## Extension Patterns
When extending the Trello MCP server with new functionality, follow these patterns:
### Adding a New Trello Entity
1. Create a new service class in `src/services/`.
2. Extend the base service class.
3. Implement entity-specific methods.
4. Add any necessary type definitions in `src/types/trello-types.ts`.
5. Create tool definitions in `src/tools/entity-tools.ts`.
6. Implement tool handlers in `src/tools/entity-tool-handlers.ts`.
7. Add the tools to the combined tools list in `src/tools/trello-tools.ts`.
8. Add the handlers to the combined handlers in `src/tools/trello-tool-handlers.ts`.
### Adding New Tools for Existing Entities
1. Add new tool definitions to the appropriate `src/tools/entity-tools.ts` file.
2. Implement handlers in the corresponding `src/tools/entity-tool-handlers.ts` file.
3. Update the combined tools and handlers files.
## Testing Your Trello MCP Server
To test your Trello MCP server:
1. Set up environment variables in `.env` file:
```
TRELLO_API_KEY=your_api_key
TRELLO_TOKEN=your_token
```
2. Build the project: `npm run build`
3. Run the server: `npm start`
4. Test with an MCP client or using the MCP CLI tool.
## Common Pitfalls to Avoid
1. **Monolithic Files**: Avoid putting too much functionality in a single file.
2. **Tight Coupling**: Avoid tight coupling between components.
3. **Inconsistent Error Handling**: Ensure consistent error handling throughout the codebase.
4. **Missing Documentation**: Document all components and extension points.
5. **Hardcoded Configuration**: Use the configuration system instead of hardcoding values.
6. **Console Output in Stdout**: Redirect all console output to stderr to avoid interfering with the MCP protocol.
By following these guidelines, you will create a Trello MCP server that is modular, maintainable, and easy to extend.